﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using UBCon.Core.Enums;
using UBCon.Ribbon.Interfaces;

namespace UBCon.Ribbon
{
    [TemplatePart(Name=launcherButtonName, Type=typeof(ButtonBase))]
    public sealed class RibbonGroup : ItemsControl, IRibbonControl
    {
        #region Constructors
        static RibbonGroup()
        {
            Type ownerType = typeof(RibbonGroup);
            RibbonGroup.DefaultStyleKeyProperty.OverrideMetadata(ownerType,
                new FrameworkPropertyMetadata(ownerType));
        }
        public RibbonGroup()
            : base()
        {
           Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(this, new MouseButtonEventHandler(OnOutsideCaptured));
        }
        #endregion

        private void OnOutsideCaptured(object sender, MouseButtonEventArgs e) 
        {
            if (IsDropDownOpen) 
            {
                IsDropDownOpen = false;
            }
        }

        #region Properties

        /// <summary>
        /// Indicates whether drop down content can be animated or not
        /// </summary>
        internal bool ContentCanBeAnimated
        {
            get
            {
                return _dropDownPopup != null
                        && _dropDownPopup.Child != null
                            && _dropDownPopup.Child is FrameworkElement
                            && _dropDownPopup.Child.RenderTransform is TranslateTransform;
            }
        }

        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Header.  This enables animation, styling, binding, etc...
        public static readonly System.Windows.DependencyProperty HeaderProperty =
            System.Windows.DependencyProperty.Register("Header", typeof(string), typeof(RibbonGroup), new PropertyMetadata((string)null));

        public bool HasLauncher
        {
            get { return (bool)GetValue(HasLauncherProperty); }
            set { SetValue(HasLauncherProperty, value); }
        }

        // Using a DependencyProperty as the backing store for HasLauncher.  This enables animation, styling, binding, etc...
        public static readonly System.Windows.DependencyProperty HasLauncherProperty =
            System.Windows.DependencyProperty.Register("HasLauncher", typeof(bool), typeof(RibbonGroup), new PropertyMetadata(false));

        [System.ComponentModel.Bindable(true), System.ComponentModel.Browsable(false), System.ComponentModel.Category("Appearance"), System.ComponentModel.ReadOnly(true)]
        public bool Minimized
        {
            get { return (bool)GetValue(MinimizedProperty); }
        }

        // Using a DependencyProperty as the backing store for Minimized.  This enables animation, styling, binding, etc...
        internal static readonly System.Windows.DependencyPropertyKey MinimizedPropertyKey =
            System.Windows.DependencyProperty.RegisterReadOnly("Minimized", typeof(bool), typeof(RibbonGroup), new System.Windows.PropertyMetadata(false));

        internal static readonly System.Windows.DependencyProperty MinimizedProperty = MinimizedPropertyKey.DependencyProperty;

        [Bindable(true), 
        Browsable(false), 
        Category("Appearance"), 
        ReadOnly(true)]
        public bool CanMinimize
        {
            get { return (bool)GetValue(CanMinimizeProperty); }
        }
        // Using a DependencyProperty as the backing store for CanMinimize.  This enables animation, styling, binding, etc...
        internal static readonly System.Windows.DependencyPropertyKey CanMinimizePropertyKey =
            System.Windows.DependencyProperty.RegisterReadOnly("CanMinimize", typeof(bool), typeof(RibbonGroup), new System.Windows.PropertyMetadata(true));

        internal static readonly System.Windows.DependencyProperty CanMinimizeProperty = CanMinimizePropertyKey.DependencyProperty;

        internal RibbonControlSize Size
        {
            get { return (RibbonControlSize)GetValue(SizeProperty); }
            set { SetValue(SizeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Size.  This enables animation, styling, binding, etc...
        internal static readonly System.Windows.DependencyProperty SizeProperty =
            System.Windows.DependencyProperty.Register("Size", typeof(RibbonControlSize), typeof(RibbonGroup),
            new System.Windows.FrameworkPropertyMetadata(
                RibbonControlSize.Large));

        public ImageSource Image
        {
            get { return (ImageSource)GetValue(ImageProperty); }
            set { SetValue(ImageProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Image.  This enables animation, styling, binding, etc...
        public static readonly System.Windows.DependencyProperty ImageProperty =
            System.Windows.DependencyProperty.Register("Image", typeof(ImageSource), typeof(RibbonGroup), new FrameworkPropertyMetadata((ImageSource)null));

        public bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }
           private set { SetValue(IsDropDownOpenProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsDropDownOpen.  This enables animation, styling, binding, etc...
        public static readonly System.Windows.DependencyProperty IsDropDownOpenProperty = DropDownButtonBase.IsDropDownOpenProperty.AddOwner(typeof(RibbonGroup), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(RibbonGroup.OnIsDropDownOpenPropertyChanged)));

        /// <summary>
        /// Gets drop down's content
        /// </summary>
        internal FrameworkElement DropDownContent
        {
            get { return _dropDownPopup.Child as FrameworkElement; }
        }

        #endregion

        #region Depenency Property Changed Callbacks

        private static void OnIsDropDownOpenPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            RibbonGroup group = (RibbonGroup)sender;
            bool newValue = (bool)e.NewValue;
            bool oldValue = !newValue;
            if (newValue)
            {
                group.TryAnimateDropDownContent();
            }
            else
            {
                if (Mouse.Captured != null)
                {
                    Visual vis = Mouse.Captured as Visual;
                    if (vis == group)
                    {
                        Mouse.Capture(null);

                        group.ReleaseMouseCapture();
                    }
                }
                group.RestoreContent();
            }
        }

        #endregion

        #region Overrieds

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (launcherButton != null) 
            {
                launcherButton.Click -= launcherButton_Click;
            }

            _dropDownPopup = this.GetTemplateChild(PART_DropDownButtonPopupName) as Popup;

            if (ContentCanBeAnimated)
            {
                RestoreContent();
            }
            launcherButton = GetTemplateChild(launcherButtonName) as ButtonBase;
            
            if (launcherButton != null && Minimized) 
            {
                launcherButton.Click += launcherButton_Click;
            }
        }

        private void launcherButton_Click(object sender, RoutedEventArgs e)
        {
            if (IsDropDownOpen) 
            {
                IsDropDownOpen = false;
            }
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is IRibbonControl;
        }
        #endregion

        #region Private Utilities

        internal void RestoreContent()
        {
            if (HideContentStoryboard == null)
            {
                BuildHideContentStoryboard();
            }
            HideContentStoryboard.Begin();
        }

        internal void TryAnimateDropDownContent()
        {
            if (ContentCanBeAnimated)
            {
                if (ShowContentStoryboard == null)
                {
                    BuildShowContentAnimation();
                }
                ShowContentStoryboard.Begin();
            }
        }

        private void BuildShowContentAnimation()
        {
            DoubleAnimation da = new DoubleAnimation() { EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseOut, Exponent = 5 } };
            TranslateTransform transform = this._dropDownPopup.Child.RenderTransform as TranslateTransform;
            Storyboard.SetTargetProperty(da, new PropertyPath(propertyPath));
            Storyboard.SetTarget(da, DropDownContent);
            ShowContentStoryboard = new Storyboard();
            da.From = -5D;
            da.To = 0.0D;
            da.Duration = new Duration(TimeSpan.FromSeconds(.20));
            ShowContentStoryboard.Children.Add(da);
            ShowContentStoryboard.Completed += ShowContentStoryboard_Completed;
        }

        void ShowContentStoryboard_Completed(object sender, EventArgs e)
        {
            if (!IsMouseCaptured)
            {
                Mouse.Capture(this, CaptureMode.SubTree);
            }
        }

        private void BuildHideContentStoryboard()
        {
            HideContentStoryboard = new Storyboard();
            DoubleAnimation da = new DoubleAnimation();
            Storyboard.SetTargetProperty(da, new PropertyPath(propertyPath));
            Storyboard.SetTarget(da, this.DropDownContent);
            da.From = 0.0D;
            da.To = -5;
            da.Duration = new Duration(TimeSpan.FromSeconds(0.02));
            HideContentStoryboard.Children.Add(da);
        }
        #endregion

        #region Event Handlers

        #endregion

     
        #region Fields
        internal Popup _dropDownPopup;
        private const string propertyPath = "(UIElement.RenderTransform).(TranslateTransform.Y)";
        private const string PART_DropDownButtonPopupName = "PART_DropDownButtonPopup";
        private Storyboard ShowContentStoryboard, HideContentStoryboard;
        private const string launcherButtonName = "PART_Launcher";
        ButtonBase launcherButton;
        #endregion
      
        #region IRibbonControl Implementation

        /// <summary>
        /// Gets a boolean value indicating whether the contol's size is large
        /// </summary>
        public bool IsLarge
        {
            get { return true; }
        }

      #endregion
        
              
    }
}
